package timetabling;

import common.RandomGenerator;

import generalEvolutionaryAlgorithm.EAInstance;
import generalEvolutionaryAlgorithm.EAMutation;

public class TimetablingMutator1 implements EAMutation {
	private boolean[][] studentAttendsEvent;
	private boolean[][] eventCanTakePartInRoom;
	private int events, rooms;
	public TimetablingMutator1(boolean[][] studentAttendsEvent, boolean[][] eventCanTakePartInRoom) {
		this.studentAttendsEvent = studentAttendsEvent;
		this.eventCanTakePartInRoom = eventCanTakePartInRoom;
		events = this.studentAttendsEvent[0].length;
		rooms = this.eventCanTakePartInRoom[0].length;
	}

	@Override
	public EAInstance mutate(EAInstance eaInstance) {
		int operations = RandomGenerator.getPoissonVariable() + 1;
		for(int operation = 0; operation < operations; ++operation){
			eaInstance = singleMutation(eaInstance);
		}
		return eaInstance;
	}

	private EAInstance singleMutation(EAInstance eaInstance){
		int days = TimetablingConstants.DAYS;
		int hours = TimetablingConstants.HOURS;
		int spots = days * hours;
		TimetablingInstance instance = (TimetablingInstance) eaInstance;
		int[] roomForEvent = instance.getRoomForEvent();
		int[] timeForEvent = instance.getTimeForEvent();
		int[][] lecturesInRooms = new int[rooms][spots];
		for(int event = 0; event < events; event++){
			if(roomForEvent[event] != -1){
				lecturesInRooms[roomForEvent[event]][timeForEvent[event]]++;
			}
		}
		if(RandomGenerator.getInt(2) == 0){
			for(int i = 0; i < events; i++){
				int event1 = RandomGenerator.getInt(events);
				int event2 = RandomGenerator.getInt(events);
				if(event1 == event2)continue;
				if(roomForEvent[event1] == -1)continue;
				if(roomForEvent[event2] == -1)continue;
				if(eventCanTakePartInRoom[event1][roomForEvent[event2]]){
					if(eventCanTakePartInRoom[event2][roomForEvent[event1]]){
						int tmp = roomForEvent[event1];
						roomForEvent[event1] = roomForEvent[event2];
						roomForEvent[event2] = tmp;
						tmp = timeForEvent[event1];
						timeForEvent[event1] = timeForEvent[event2];
						timeForEvent[event2] = tmp;
						return  new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
					}
				}

			}
			int goodPairs = 0;
			for(int event1 = 0; event1 < events; ++event1){
				if(roomForEvent[event1] == -1)continue;
				for(int event2 = event1 + 1; event2 < events; event2++){
					if(roomForEvent[event2] == -1)continue;
					if(eventCanTakePartInRoom[event1][roomForEvent[event2]]){
						if(eventCanTakePartInRoom[event2][roomForEvent[event1]]){
							++goodPairs;
						}
					}
				}
			}
			if(goodPairs > 0){
				int choosenPair = RandomGenerator.getInt(goodPairs);
				for(int event1 = 0; event1 < events; ++event1){
					if(roomForEvent[event1] == -1)continue;
					for(int event2 = event1 + 1; event2 < events; event2++){
						if(roomForEvent[event2] == -1)continue;
						if(eventCanTakePartInRoom[event1][roomForEvent[event2]]){
							if(eventCanTakePartInRoom[event2][roomForEvent[event1]]){
								--choosenPair;
								if(choosenPair < 0){
									int tmp = roomForEvent[event1];
									roomForEvent[event1] = roomForEvent[event2];
									roomForEvent[event2] = tmp;
									tmp = timeForEvent[event1];
									timeForEvent[event1] = timeForEvent[event2];
									timeForEvent[event2] = tmp;
									return  new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
								}
							}
						}
					}
				}
				
			}
		}
		while(true){
			int r = RandomGenerator.getInt(rooms);
			int e = RandomGenerator.getInt(events);
			for(int room = 0; room < rooms; ++room){
				++r;
				if(r == rooms)r = 0;
				for(int event = 0; event < events; ++event){
					++e;
					if(e == events)e = 0;
					if(eventCanTakePartInRoom[e][r]){
						if(r == roomForEvent[e]){
							roomForEvent[e] = -1;
							timeForEvent[e] = -1;
							return new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
						} else {
							int t = RandomGenerator.getInt(spots);
							for(int time = 0; time < spots; time++){
								++t;
								if(t == spots)t = 0;
								if(lecturesInRooms[r][t] == 0){
									roomForEvent[e] = r;
									timeForEvent[e] = t;
									return  new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
									//				tmp.calculateResult();
									//				System.out.println(tmp.toString() + tmp.getResult().optimalValue.toString());
								}
							}
						}
					}
				}
			}
		}
	}
}
